//
//  NSObject+AviodCrash.m
//  LzlPictureBrower
//
//  Created by luozhilin on 2018/6/5.
//  Copyright © 2018年 luozhilin. All rights reserved.
//

#import "NSObject+AviodCrash.h"
#import <objc/runtime.h>
#import "YYAvoidCrash.h"
#import "YYAvoidCrashStubProxy.h"

@implementation NSObject (AviodCrash)

+ (void)avoidCrashExchangeMethodIfDealWithNoneSel:(BOOL)ifDealWithNoneSel
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        //setValue:forKey:
        [YYAvoidCrash exchangeInstanceMethod:[self class] originalMethod:@selector(setValue:forKey:) newMethod:@selector(yy_avoidCrashSetValue:forKey:)];
        
        //setValue:forKeyPath:
        [YYAvoidCrash exchangeInstanceMethod:[self class] originalMethod:@selector(setValue:forKeyPath:) newMethod:@selector(yy_avoidCrashSetValue:forKeyPath:)];
        
        //setValue:forUndefinedKey:
        [YYAvoidCrash exchangeInstanceMethod:[self class] originalMethod:@selector(setValue:forUndefinedKey:) newMethod:@selector(yy_avoidCrashSetValue:forUndefinedKey:)];
        
        //setValuesForKeysWithDictionary:
        [YYAvoidCrash exchangeInstanceMethod:[self class] originalMethod:@selector(setValuesForKeysWithDictionary:) newMethod:@selector(yy_avoidCrashSetValuesForKeysWithDictionary:)];
        
        //unrecognized selector sent to instance  消息转发
        if (ifDealWithNoneSel) {
            [YYAvoidCrash exchangeInstanceMethod:[self class] originalMethod:@selector(methodSignatureForSelector:) newMethod:@selector(yy_avoidCrashMethodSignatureForSelector:)];
            [YYAvoidCrash exchangeInstanceMethod:[self class] originalMethod:@selector(forwardInvocation:) newMethod:@selector(yy_avoidCrashForwardInvocation:)];
        }
    });
}

#pragma mark - 没有实现相应的方法，实现消息转发，防止崩溃
static NSMutableArray *noneSelClassStrings;
static NSMutableArray *noneSelClassStringPrefixs;

+ (void)setupNoSelClassAvoidCrashWithClassNameArray:(NSArray<NSString *> *)classArray {
    
    if (noneSelClassStrings) {
        
        NSString *warningMsg = [NSString stringWithFormat:@"\n\n%@\n\n[AvoidCrash setupNoneSelClassStringsArr:];\n调用一此即可，多次调用会自动忽略后面的调用\n\n%@\n\n",AvoidCrashSeparatorWithFlag,AvoidCrashSeparator];
        YYAvoidCrashLog(@"%@",warningMsg);
    }
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        noneSelClassStrings = [NSMutableArray array];
        for (NSString *className in classArray) {
            if ([className hasPrefix:@"UI"] == NO &&
                [className isEqualToString:NSStringFromClass([NSObject class])] == NO) {
                [noneSelClassStrings addObject:className];
                
            } else {
                NSString *warningMsg = [NSString stringWithFormat:@"\n\n%@\n\n[AvoidCrash setupNoneSelClassStringsArr:];\n会忽略UI开头的类和NSObject类(请使用NSObject的子类)\n\n%@\n\n",AvoidCrashSeparatorWithFlag,AvoidCrashSeparator];
                YYAvoidCrashLog(@"%@",warningMsg);
            }
        }
    });
}

/**
 *  初始化一个需要防止”unrecognized selector sent to instance”的崩溃的类名前缀的数组
 */
+ (void)setupNoSelClassAvoidCrashWithPrefixClassNameArray:(NSArray<NSString *> *)prefixClassArray {
    if (noneSelClassStringPrefixs) {
        
        NSString *warningMsg = [NSString stringWithFormat:@"\n\n%@\n\n[AvoidCrash setupNoneSelClassStringPrefixsArr:];\n调用一此即可，多次调用会自动忽略后面的调用\n\n%@\n\n",AvoidCrashSeparatorWithFlag,AvoidCrashSeparator];
        YYAvoidCrashLog(@"%@",warningMsg);
    }
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        noneSelClassStringPrefixs = [NSMutableArray array];
        for (NSString *classNamePrefix in prefixClassArray) {
            if ([classNamePrefix hasPrefix:@"UI"] == NO &&
                [classNamePrefix hasPrefix:@"NS"] == NO) {
                [noneSelClassStringPrefixs addObject:classNamePrefix];
                
            } else {
                NSString *warningMsg = [NSString stringWithFormat:@"\n\n%@\n\n[AvoidCrash setupNoneSelClassStringsArr:];\n会忽略UI开头的类和NS开头的类\n若需要对NS开头的类防止”unrecognized selector sent to instance”(比如NSArray),请使用setupNoneSelClassStringsArr:\n\n%@\n\n",AvoidCrashSeparatorWithFlag,AvoidCrashSeparator];
                YYAvoidCrashLog(@"%@",warningMsg);
            }
        }
    });
}

- (NSMethodSignature *)yy_avoidCrashMethodSignatureForSelector:(SEL)aSelector {
    
    NSMethodSignature *ms = [self yy_avoidCrashMethodSignatureForSelector:aSelector];
    
    BOOL flag = NO;
    if (ms == nil) {
        for (NSString *classStr in noneSelClassStrings) {
            if ([self isKindOfClass:NSClassFromString(classStr)]) {
                ms = [YYAvoidCrashStubProxy instanceMethodSignatureForSelector:@selector(proxyMethod)];
                flag = YES;
                break;
            }
        }
    }
    if (flag == NO) {
        NSString *selfClass = NSStringFromClass([self class]);
        for (NSString *classStrPrefix in noneSelClassStringPrefixs) {
            if ([selfClass hasPrefix:classStrPrefix]) {
                ms = [YYAvoidCrashStubProxy instanceMethodSignatureForSelector:@selector(proxyMethod)];
            }
        }
    }
    return ms;
}

- (void)yy_avoidCrashForwardInvocation:(NSInvocation *)anInvocation {
    
    @try {
        [self yy_avoidCrashForwardInvocation:anInvocation];
        
    } @catch (NSException *exception) {
        NSString *defaultToDo = AvoidCrashDefaultIgnore;
        [YYAvoidCrash errorMessageWithException:exception defaultToDo:defaultToDo];
    } @finally {
        
    }
    
}



#pragma mark - 常用方法的防止崩溃
- (void)yy_avoidCrashSetValue:(id)value forKey:(NSString *)key
{
    @try {
        [self yy_avoidCrashSetValue:value forKey:key];
    }@catch (NSException *exception) {
        NSString *defaultToDo = AvoidCrashDefaultIgnore;
        [YYAvoidCrash errorMessageWithException:exception defaultToDo:defaultToDo];
    } @finally {
        
    }
}

- (void)yy_avoidCrashSetValue:(id)value forKeyPath:(NSString *)key
{
    @try {
        [self yy_avoidCrashSetValue:value forKeyPath:key];
    }@catch (NSException *exception) {
        NSString *defaultToDo = AvoidCrashDefaultIgnore;
        [YYAvoidCrash errorMessageWithException:exception defaultToDo:defaultToDo];
    } @finally {
        
    }
}

- (void)yy_avoidCrashSetValue:(id)value forUndefinedKey:(NSString *)key {
    @try {
        [self yy_avoidCrashSetValue:value forUndefinedKey:key];
    }@catch (NSException *exception) {
        NSString *defaultToDo = AvoidCrashDefaultIgnore;
        [YYAvoidCrash errorMessageWithException:exception defaultToDo:defaultToDo];
    }@finally {
        
    }
}

- (void)yy_avoidCrashSetValuesForKeysWithDictionary:(NSDictionary<NSString *,id> *)keyedValues {
    @try {
        [self yy_avoidCrashSetValuesForKeysWithDictionary:keyedValues];
    }@catch (NSException *exception) {
        NSString *defaultToDo = AvoidCrashDefaultIgnore;
        [YYAvoidCrash errorMessageWithException:exception defaultToDo:defaultToDo];
    }@finally {
        
    }
}

@end
